/**
 * Created by ${PRODUCT_NAME}.
 * User: skylerparr
 * Date: 12/27/10
 * Time: 8:28 AM
 * To change this template use File | Settings | File Templates.
 */
package com.thoughtorigin.data {
import com.thoughtorigin.application.ApplicationSettings;
import com.thoughtorigin.application.FeedItem;
import com.thoughtorigin.loader.AbstractLoader;
import com.thoughtorigin.loader.AbstractLoader;
import com.thoughtorigin.loader.AbstractLoader;
import com.thoughtorigin.loader.URLLoaderItem;
import com.thoughtorigin.util.Instantiator;

import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.utils.Dictionary;

public class CachedDataLoader implements DataLoader {

    private var _instantiator: Instantiator;
    private var _dataCache: DataCache;
    private var _applicationSettings: ApplicationSettings;
    private var _loadingDict: Dictionary;

    public function CachedDataLoader() {
        _loadingDict = new Dictionary(true);
    }

    public function loadValueObject(valueClass:Class, callback:Function, nocache:Boolean = false, failFunction: Function = null):void {
        var retVal: Object = instantiate(valueClass, null);
        if(retVal is ValueObject) {
            var valueObject: ValueObject = ValueObject(retVal);
            var fi: FeedItem = _applicationSettings.getFeedById(valueObject.feedName);
            var url: String = _applicationSettings.baseFeedUrl + fi.url;
            if(_dataCache) {
                if(_dataCache.isAssetCached(url)) {
                    if(retVal is ValueObject) {
                        ValueObject(retVal).populate(_dataCache.getAsset(url), function(): void {
                            callback.call(this, retVal);
                        });
                        return;
                    }
                    callback.call(this, retVal);
                    return;
                }
            }
            loadUrl(valueObject, url, callback, nocache, failFunction, fi.method);
        }
    }

    public function loadObjectFromUrl(valueClass:Class, url:String, callback:Function, nocache:Boolean = false, failFunction: Function = null):void {
        var retVal: Object = instantiate(valueClass, null);
        if(_dataCache) {
            if(_dataCache.isAssetCached(url)) {
                callback.call(this, _dataCache.getAsset(url));
                return;
            }
        }
        if(retVal is ValueObject) {
            var valueObject: ValueObject = ValueObject(retVal);
            var fi: FeedItem = _applicationSettings.getFeedById(valueObject.feedName);
            url = _applicationSettings.baseFeedUrl + fi.url + "" + url;
        }
        loadUrl(retVal, url, callback, nocache, failFunction, fi.method);
    }

    private function loadUrl(object: Object, url: String, callback: Function, nocache: Boolean, failFunction: Function, requestMethod: String): void {
        var loader: AbstractLoader = createLoader(url);
        loader.requestMethod = requestMethod;
        var succussFunction: Function = function(e: Event): void {
            removeListeners(loader, succussFunction, failFunction);
            if(object is ValueObject) {
                ValueObject(object).populate(loader.content, function(): void {
                    var callbackFunc: Function = _loadingDict[url];
                    delete _loadingDict[url];
                    if(_dataCache && !nocache && !_dataCache.isAssetCached(url)) {
                        _dataCache.cacheAsset(loader);
                    }
                    if(callbackFunc != null) {
                        callbackFunc.call(this, object);
                    }
                });
            }
        }
        var ff: Function = function(e: Event): void {
            removeListeners(loader, succussFunction, ff);
            delete _loadingDict[url];
            if(failFunction != null) {
                failFunction.call(this, null);
            }
        }

        addListeners(loader, succussFunction, ff);

        if(_loadingDict[url]) {
            return;
        }
        _loadingDict[url] = callback;
        loader.start();
    }

    protected function addListeners(loader: AbstractLoader, successFunc: Function, failFunc: Function): void {
        loader.addEventListener(Event.COMPLETE, successFunc);
        if(failFunc == null) return;
        loader.addEventListener(IOErrorEvent.DISK_ERROR, failFunc);
        loader.addEventListener(IOErrorEvent.IO_ERROR, failFunc);
        loader.addEventListener(IOErrorEvent.NETWORK_ERROR, failFunc);
        loader.addEventListener(IOErrorEvent.VERIFY_ERROR, failFunc);
        loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, failFunc);
    }

    protected function removeListeners(loader: AbstractLoader, successFunc: Function, failFunc: Function): void {
        loader.removeEventListener(Event.COMPLETE, successFunc);
        if(failFunc == null) return;
        loader.removeEventListener(IOErrorEvent.DISK_ERROR, failFunc);
        loader.removeEventListener(IOErrorEvent.IO_ERROR, failFunc);
        loader.removeEventListener(IOErrorEvent.NETWORK_ERROR, failFunc);
        loader.removeEventListener(IOErrorEvent.VERIFY_ERROR, failFunc);
        loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, failFunc);
    }

    protected function createLoader(url: String): AbstractLoader {
        return new URLLoaderItem(url);
    }

    protected function instantiate(clazz: Class, args: Array): Object {
        return _instantiator.create(clazz, args);
    }

    [Inject]
    public function set dataCache(value: DataCache): void {
        _dataCache = value;
    }

    [Inject]
    public function set instantiator(value: Instantiator): void {
        _instantiator = value;
    }

    [Inject]
    public function set applicationSettings(value: ApplicationSettings): void {
        _applicationSettings = value;
    }
}
}
